package com.gxc.dbcore.dao.impl;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.beanutils.PropertyUtils;
import org.apache.ibatis.builder.xml.dynamic.ForEachSqlNode;
import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.executor.ExecutorException;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.ParameterMode;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.property.PropertyTokenizer;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.type.TypeHandler;
import org.apache.ibatis.type.TypeHandlerRegistry;
import org.apache.log4j.Logger;
import org.mybatis.spring.SqlSessionTemplate;

public class SqlDaoCountImpl {

	private static Logger logger = Logger.getLogger(SqlDaoCountImpl.class);

	@SuppressWarnings("rawtypes")
	public static long count(SqlSessionTemplate session, String statement,
			Object parameter) {

		long count = 0L;
		Map parameterMap = SqlDaoCountImpl.toParameterMap(parameter);
		Connection connection = null;
		String sql = "";
		try {
			MappedStatement mst = session.getConfiguration()
					.getMappedStatement(statement);
			BoundSql boundSql = mst.getBoundSql(parameterMap);
			sql = "select count(*) as total_count from (" + boundSql.getSql()
					+ ")";
			// 打印日志
			StringBuffer logBuffer1 = new StringBuffer();
			logBuffer1.append("\n查询sql：\n");
			logBuffer1.append(sql);
			logBuffer1.append("\n查询参数：\n");
			if (parameterMap != null) {
				for (Object key : parameterMap.keySet()) {
					logBuffer1.append(key.toString());
					logBuffer1.append("：");
					logBuffer1.append(parameterMap.get(key));
					logBuffer1.append("\n");
				}
			}
			logger.debug(logBuffer1.toString());

			connection = session.getConfiguration().getEnvironment()
					.getDataSource().getConnection();
			PreparedStatement pstmt = connection.prepareStatement(sql);
			SqlDaoCountImpl.setParameters(pstmt, mst, boundSql, parameterMap);
			ResultSet rs = pstmt.executeQuery();
			if (rs.next()) {
				try {
					count = rs.getLong("total_count");
				} catch (Exception e) {
					count = rs.getLong("TOTAL_COUNT");
				}
			}
		} catch (Exception e) {
			count = 0L;
			e.printStackTrace();
			throw new RuntimeException(e);
		} finally {
			if (connection != null) {
				try {
					connection.close();
					connection = null;
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
		}
		StringBuffer logBuffer2 = new StringBuffer();
		logBuffer2.append("\nCount结果：");
		logBuffer2.append(count);
		logBuffer2.append("\n");
		logger.debug(logBuffer2.toString());
		return count;
	}

	/**
	 * 对SQL参数(?)设值,参考org.apache.ibatis.executor.parameter.
	 * DefaultParameterHandler
	 * 
	 * @param ps
	 * @param mappedStatement
	 * @param boundSql
	 * @param parameterObject
	 * @throws SQLException
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	protected static void setParameters(PreparedStatement ps,
			MappedStatement mappedStatement, BoundSql boundSql,
			Object parameterObject) throws SQLException {
		ErrorContext.instance().activity("setting parameters")
				.object(mappedStatement.getParameterMap().getId());
		List<ParameterMapping> parameterMappings = boundSql
				.getParameterMappings();
		if (parameterMappings != null) {
			Configuration configuration = mappedStatement.getConfiguration();
			TypeHandlerRegistry typeHandlerRegistry = configuration
					.getTypeHandlerRegistry();
			MetaObject metaObject = parameterObject == null ? null
					: configuration.newMetaObject(parameterObject);
			for (int i = 0; i < parameterMappings.size(); i++) {
				ParameterMapping parameterMapping = parameterMappings.get(i);
				if (parameterMapping.getMode() != ParameterMode.OUT) {
					Object value;
					String propertyName = parameterMapping.getProperty();
					PropertyTokenizer prop = new PropertyTokenizer(propertyName);
					if (parameterObject == null) {
						value = null;
					} else if (typeHandlerRegistry
							.hasTypeHandler(parameterObject.getClass())) {
						value = parameterObject;
					} else if (boundSql.hasAdditionalParameter(propertyName)) {
						value = boundSql.getAdditionalParameter(propertyName);
					} else if (propertyName
							.startsWith(ForEachSqlNode.ITEM_PREFIX)
							&& boundSql.hasAdditionalParameter(prop.getName())) {
						value = boundSql.getAdditionalParameter(prop.getName());
						if (value != null) {
							value = configuration.newMetaObject(value)
									.getValue(
											propertyName.substring(prop
													.getName().length()));
						}
					} else {
						value = metaObject == null ? null : metaObject
								.getValue(propertyName);
					}
					TypeHandler typeHandler = parameterMapping.getTypeHandler();
					if (typeHandler == null) {
						throw new ExecutorException(
								"There was no TypeHandler found for parameter "
										+ propertyName + " of statement "
										+ mappedStatement.getId());
					}
					typeHandler.setParameter(ps, i + 1, value,
							parameterMapping.getJdbcType());
				}
			}
		}
	}

	@SuppressWarnings("rawtypes")
	protected static Map toParameterMap(Object parameter) {
		if (parameter == null) {
			return new HashMap();
		}

		if (parameter instanceof Map) {
			return (Map<?, ?>) parameter;
		} else {
			try {
				return PropertyUtils.describe(parameter);
			} catch (Exception e) {
				e.printStackTrace();
				return null;
			}
		}
	}
}
